﻿using Styx;
using System;
using Styx.Helpers;
using Styx.Pathing;
using System.Threading;
using System.Diagnostics;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Styx.Common;
using Styx.Plugins;
using System.Linq;
using System.Windows.Media;
using System.Windows.Forms;
using Styx.CommonBot.POI; 
using Bots.ArchaeologyBuddy;
using System.Text.RegularExpressions;
using System.Drawing;
using Styx.CommonBot;
using GreyMagic;
using Styx.Common.Helpers;
using Styx.CommonBot.Inventory;
using Styx.Localization;
using System.Media;
using System.Xml.Linq;
using System.Xml;
using System.Net;
using System.Globalization;
using Styx.CommonBot.Frames;
using Styx.WoWInternals.DBC;
using CombatPuller.Settings;
using Styx.CommonBot.Profiles;
//using CombatPuller.WayPoints;

//CombatPuller
namespace CombatPuller
{
	

   public class Main : HBPlugin

    {
		private Form _gui;
        private static LocalPlayer Me { get { return StyxWoW.Me; } }
        public override string Author { get { return "PePPeRmix"; }}
        public override string ButtonText { get { return "Configure"; } }
        public override string Name { get { return "Combat Puller"; }}
        public override Version Version { get { return new Version(0,0,4); }}
		private Stopwatch runtime = new Stopwatch();
		private static string SvnRevision { get { return "$Rev: 004 $"; } }
		private static bool looting = false;
        public override bool WantButton { get { return true; } }
		private static WoWGuid guid;
		private List<uint> bList = new List<uint>();
		private List<uint> fList = new List<uint>();
		private static bool _okpulling = false;
		//private static bool _KillAll = false;
		private static DateTime _PullSuspendedTime = DateTime.UtcNow;
		public WoWUnit debugUnit;
		public int stepToEnd = 0;
		private static DateTime _StopPullingTime = DateTime.UtcNow;
		private static DateTime _treasureTO = DateTime.UtcNow;
		private static DateTime _WPListCheck = DateTime.UtcNow;
		public int totalpulls = 0;
		public List<WoWUnit> pulledList = new List<WoWUnit>();
		public WoWUnit unit;
		public WoWUnit unitL;
		public List<WayPoint> wpList = new List<WayPoint>();
		

		public override void OnButtonPress()
        {
            if (_gui == null || _gui.IsDisposed || _gui.Disposing) _gui = new GUI(this);
            if (_gui != null || _gui.IsDisposed) _gui.ShowDialog();
        }
		
		
		
		public void set_init()
		{
				init_tList(bList, Settings.CPSettings.Instance.BList); //Inititalize BLACKLIST
				init_tList(fList, Settings.CPSettings.Instance.FList); //Inititalize Faction List
                Logging.Write(Colors.Fuchsia, "Combat Puller Settings [{0}]", SvnRevision.Replace("$", "")); // Will print as [ Rev: 1 ]
				Logging.Write(Colors.Fuchsia, "SETTINGS: [Max Distance: {0} yds] [Max Mobs: {1}]  [Max HP: {2}]", Settings.CPSettings.Instance.PullRange, 
				Settings.CPSettings.Instance.MobMax, Settings.CPSettings.Instance.MaxHealth); 
				Logging.Write(Colors.Fuchsia, "Faction List: [{0}]", Settings.CPSettings.Instance.FList);
				Logging.Write(Colors.Fuchsia, "Black List: [{0}]", Settings.CPSettings.Instance.BList);
		}
		
		public override void OnEnable()
        {
				set_init();
				wpList.Clear();
				base.OnEnable();
		}
        public override void OnDisable()
        {
            wpList.Clear();
            base.OnDisable();
        }
 
		private void init_tList(List<uint> tList, string tString)
		{
			tList.Clear();
			string sentence = tString;
			string[] digits = Regex.Split(sentence, @"\D+");
			foreach (string value in digits)
			{
				uint number;
				if (uint.TryParse(value, out number))
				{
					tList.Add(number);
				}
			}
			if (tList.Count() <= 0) tList.Add(0);
		}
        
	public override void Pulse() 
    {
		bool stopRT = false;
		if 	(Settings.CPSettings.Instance.RunTime != 0)
		{
			if (!runtime.IsRunning) runtime.Start();
			if (runtime.Elapsed.TotalMinutes > Settings.CPSettings.Instance.RunTime) 
			{
				stopRT = true;
				if (runtime.IsRunning) runtime.Stop();
			}
		}
			
		if 	(!stopRT)
		{
			
				if (DateTime.UtcNow > _treasureTO.AddMilliseconds(2000))
				{
					_treasureTO = DateTime.UtcNow;
					UseItemId(87391);
				}
				
			
					//Logging.Write(Colors.Fuchsia, "[ PULSE ] [ 1 ]");
					if (Settings.CPSettings.Instance.LootInCombat) lootNow();
					
					AquireTarget();
					//Logging.Write(Colors.Fuchsia, "[ PULSE ] [ 2 ]");
					if (!Settings.CPSettings.Instance.LootInCombat && Lootables.Count() > Settings.CPSettings.Instance.MaxCorpse)
					{
						_okpulling = false;
						looting = true;
						//Logging.Write(Colors.Fuchsia, "[ PULSE ] [ 3 ]");
					}
					
					if (Me.Combat && 
						(totalpulls > Settings.CPSettings.Instance.MobMax || Me.HealthPercent < Settings.CPSettings.Instance.MaxHealth)
						|| NearbyUnfriendlyUnits.Count() > Settings.CPSettings.Instance.MobMax)
						{
						_okpulling = false;
						//Logging.Write(Colors.Fuchsia, "[ PULSE ] [ 4 ]");
						}
					
					if (!Me.Combat && (Lootables.Count() < Settings.CPSettings.Instance.MaxCorpse || Settings.CPSettings.Instance.LootInCombat))
						{
						_okpulling = true;
						looting = false;
						totalpulls = 0;
						//Logging.Write(Colors.Fuchsia, "[ PULSE ] [ 5 ]");
						}
					
					/////////////// PULL /////////////////////////////
					if (_okpulling)
					{
						if (totalpulls > Settings.CPSettings.Instance.MobMax || Me.HealthPercent < Settings.CPSettings.Instance.MaxHealth)
						{
						_okpulling = false;
						} else
						{
							Pull();
						}
					}else if(looting)
					{
						lootNow();
					}
			}
		if (DateTime.UtcNow > _WPListCheck.AddMilliseconds(500) && wpList.Count() > 0)
		{
			for (int i=wpList.Count()-1; i>=0; i--)
					{
						if (/*wpList[i].guid == 0 ||*/ wpList[i].guid == null) 
						{
							wpList.RemoveAt(i);
							continue;
						}
						
						WoWUnit cUnit = ObjectManager.GetObjectByGuid<WoWUnit>(wpList[i].guid);
						if (cUnit == null || (cUnit != null && (cUnit.IsDead || !cUnit.IsAlive || !fightWithMe(cUnit)))) wpList.RemoveAt(i);
					}
			_WPListCheck = DateTime.UtcNow;
		}
			
		////////////////////////// END PULSE //////////////////////////////////
		}

		///////////////// PULL /////////////////////////
		
		public void Pull()
        {
          
           
		   if (Me.Combat || (Me.GotAlivePet && Me.Pet.Combat))
            {
					if (Settings.CPSettings.Instance.AllFactions)
					{
					unit = FarmAll;
					} else	{
					unit = Farm;
					}
					
					
					if (unit != null && !unit.IsDead && unit.Guid != guid && !unit.TaggedByOther && DateTime.UtcNow > _PullSuspendedTime.AddMilliseconds(2000))
                    {
						guid = unit.Guid;
						if (!Settings.CPSettings.Instance.PullInFight)
						{
							totalpulls = totalpulls + 1;
						}
						
						Logging.Write(Colors.Fuchsia, "[ PULL ] [ {0} ]    [{1}] [Distance: {2}]", totalpulls, unit.SafeName, unit.Distance);
						unit.Target();
						BotPoi.Current = new BotPoi(unit, PoiType.Kill);
						
						
						_PullSuspendedTime = DateTime.UtcNow;
						
						return;
						
                    }
			}
        }
		
		public void lootNow()
		{
			if (Me.IsCasting || (Lootables.Count() < Settings.CPSettings.Instance.MaxCorpse  && !Settings.CPSettings.Instance.LootInCombat) || Me.IsMoving)
                return;
            if (LootFrame.Instance != null && LootFrame.Instance.IsVisible)
            {
                LootFrame.Instance.LootAll();
                return;
            }
            var lootTarget = Lootables.FirstOrDefault();
            if (lootTarget != null && lootTarget.Distance > lootTarget.InteractRange)
            {
				Logging.Write(Colors.Gold, "[CP] [Move to loot {0}.]", lootTarget.Name);
                Navigator.MoveTo(lootTarget.Location);
            }
            else if (lootTarget != null)
            {
                lootTarget.Interact();
                return;
            }
		}

		private static void UseItemId(uint id)
        {
            foreach (WoWItem item in StyxWoW.Me.BagItems.Where(item => item.Entry == id).Where(CanUseItem))
            {
                UseItem(item);
                return;
            }
        }
		private static bool CanUseItem(WoWItem item)
        {
            return item.Usable && item.Cooldown <= 0;
        }
		private static void UseItem(WoWItem item)
        {
            //PuLogger.ItemLog("Using item: " + item.Name);
            if (item.Usable && item.Cooldown <= 0)
            {
                item.Use();
            }
        }
		/// TARGET ///////////////////////////////////
		
       public void AquireTarget()
        {
			if (Me.GotTarget)
			{
				WoWUnit _unitT;
				_unitT = Me.CurrentTarget as WoWUnit;
				bool rmWP = false;
				bool isWP = false;
					
				if (_unitT != null && !_unitT.IsAlive && !_unitT.Lootable)
				{
					Me.ClearTarget();
					rmWP = true;
				}
				
				
				
				if (_unitT != null && _unitT.IsAlive)
				{
					for (int i=0; i<wpList.Count(); i++)
					{
						if (wpList[i].guid == _unitT.Guid) isWP = true;
					}
					
					if ( _unitT.Distance > 25 && DateTime.UtcNow > _StopPullingTime.AddMilliseconds(1000))
					{
						if (debugUnit != null && _unitT != null && _unitT == debugUnit && stepToEnd > 2 && !fightWithMe(_unitT))
						{	
							Me.ClearTarget();
							rmWP = true;
							stepToEnd = 0;
							Logging.Write(Colors.Red, "[CP] [Target unreacheble, clear target]");
                            Blacklist.Add(_unitT.Guid, BlacklistFlags.Pull, TimeSpan.FromMilliseconds(100), "Target unreacheble");
							debugUnit = null;
						}else if (_unitT != null && (debugUnit == null || _unitT != debugUnit) && !fightWithMe(_unitT))
						{
							debugUnit = _unitT;
							stepToEnd = 0;
						}

                        if (_unitT != null && fightWithMe(_unitT) && Blacklist.Contains(_unitT.Guid, BlacklistFlags.Pull))
						{
							Blacklist.GetEntry(_unitT.Guid).Timer.Stop();
							stepToEnd = 0;
						}
						
						stepToEnd++;
						_StopPullingTime = DateTime.UtcNow;
					}
				}
				
				
				
				if (!isWP && !rmWP)
				{
					wpList.Add(new WayPoint(_unitT.Guid, _unitT.Location));
				}
				if (rmWP)
				{
					for (int i=wpList.Count()-1; i>=0; i--)
					{
						if (wpList[i].guid == _unitT.Guid) wpList.RemoveAt(i);
					}
				}
				/*
				if (_unitT != null && BotPoi.Current != null && _unitT.IsAlive && !_unitT.IsTargetingMeOrPet && 10 < _unitT.Distance < 30 && BotPoi.Current.Guid == _unitT.Guid)
				{
					
					
				}
				*/
			}
			
			if (Settings.CPSettings.Instance.PullInFight && (Me.Combat || Me.PetInCombat))
			{
				List<WoWUnit> tempUList = takeUnits;
				
				for (int i=0; i<tempUList.Count(); i++)
				{
					if (fightWithMe(tempUList[i]) && !pulledList.Contains(tempUList[i]))
					{
						pulledList.Add(tempUList[i]);
					}
				}
				
				for (int j=pulledList.Count()-1; j>=0; j--)
				{
					//if (j<0) break;
					try{
					if ((pulledList[j] != null && pulledList[j].IsValid && (pulledList[j].IsDead || !fightWithMe(pulledList[j]) || !pulledList[j].IsAlive)) || pulledList[j] == null)
					{
						pulledList.RemoveAt(j);
					}
					}
					catch (Exception e)
					{
						// Something unexpected went wrong.
						//Logging.Write(Colors.Gold, "[ TRTTTR ] {0}", e.ToString());
						//Logging.Write(Colors.Gold, "[ TRTTTR ] {0} \nObject: {1}", e.ToString(), pulledList[j]);
						// Maybe it is also necessary to terminate / restart the application.
					}
				}
				totalpulls = pulledList.Count();
			}
			
			if ((Me.Combat || Me.PetInCombat) &&
			(!Me.GotTarget || Me.CurrentTarget.IsFriendly || Me.CurrentTarget.IsDead))
			{
            
				WoWUnit unitT;
				unitT = AqTarget;
            
				if (unitT != null && !unitT.IsDead)
				{
					unitT.Target();
					Logging.Write(Colors.Gold, "[ CP ] [ TARGET: {0}] [Distance: {1}]", unitT.SafeName, unitT.Distance);
					
				}
			}
			
        }
		
		private static bool fightWithMe(WoWUnit tUnit)
		{
			 if (tUnit.IsTargetingMyPartyMember || tUnit.IsTargetingMeOrPet)
			 {
				return true;
			 }else{
				return false;
			 }
		}
		/*
		internal static Composite CreateAttackFlyingOrUnreachableMobs()
        {
            return new Decorator(
                // changed to only do on non-player targets
                ret => {
                    if (!Me.GotTarget)
                        return false;

                    if (Me.CurrentTarget.Distance2DSqr < 25)
                    {
                        Logger.Write(Color.White, "{0} appears to be off the ground! using Ranged attack....", Me.CurrentTarget.SafeName());
                        return true;
                    }

                    return false;
                    },
                new PrioritySelector(
                    Spell.Cast("Deadly Throw", req => Me.ComboPoints > 0),
                    Spell.Cast("Shuriken Toss"),
                    Spell.Cast("Throw")
                    )
                );
        }

		internal static Composite SpellPull()
        {
            return new Decorator(
                // changed to only do on non-player targets
                ret => {
                    if (!Me.GotTarget)
                        return false;

                    if (Me.CurrentTarget.Distance2DSqr < 25)
                    {
                        Logger.Write(Color.White, "{0} appears to be off the ground! using Ranged attack....", Me.CurrentTarget.SafeName());
                        return true;
                    }

                    return false;
                    },
                new PrioritySelector(
                    Spell.Cast("Deadly Throw", req => Me.ComboPoints > 0),
                    Spell.Cast("Shuriken Toss"),
                    Spell.Cast("Throw")
                    )
                );
        }
		*/
		public WoWUnit getByGUID(WoWGuid _GUID_)
		{
			
			return ObjectManager.GetObjectByGuid<WoWUnit>(_GUID_);
			
		}
		
		public WoWUnit AqTarget
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>().Where(o => o != null && !o.IsDead && fightWithMe(o)).FirstOrDefault();
            }
		}
		
		public List<WoWUnit> takeUnits
		{
			get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>().Where(o => (
                    !o.IsDead && fightWithMe(o) && o.CanSelect && !o.IsFriendly && !o.IsNonCombatPet && !o.IsCritter)).OrderBy(o => o.Distance).ToList();
            }
		}
		
		public WoWUnit Farm
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>().Where(o => (
					isFactioned(o) && o.Distance < Settings.CPSettings.Instance.PullRange && !o.IsDead && !fightWithMe(o) && !o.TaggedByOther && 
					o.CanSelect && !o.IsFriendly && !o.IsNonCombatPet && !o.IsCritter &&
					!isBListed(o) && isPullable(o))).OrderBy(o => o.Distance).FirstOrDefault();
            }
		}	
		
		public WoWUnit FarmAll
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>().Where(o => o != null && o.Distance < Settings.CPSettings.Instance.PullRange && !o.IsDead && !fightWithMe(o) && !o.TaggedByOther &&
					o.Attackable && o.IsValid && o.CanSelect && !o.IsFriendly && !o.IsDead && !o.IsNonCombatPet && !o.IsCritter && 
					!isBListed(o) && isPullable(o)).OrderBy(o => o.Distance).FirstOrDefault();
            }
		}	
		
		public WoWUnit Lootable
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>().Where(o => o!= null && o.IsDead && o.Lootable && o.Distance <= 10).OrderBy(o => o.Distance).FirstOrDefault();
            }
		}	
		
		private bool isBListed(WoWUnit wowu)
		{
			return bList.Contains(wowu.Entry);
		}
		
		private bool isFactioned(WoWUnit wowu)
		{
			return fList.Contains(wowu.FactionId);
		}
		
		private bool isPullable(WoWUnit wowu)
		{
			if (wpList.Count() <= 0) return true;
			
			WoWPoint wp = wowu.Location;
			for (int i=0;i<wpList.Count();i++)
			{
								//Logging.Write(Colors.Fuchsia, "[ isPULL ] [ {0} ] [Distance: {1}]", wpList[i].guid, wp.Distance(wpList[i].point));
						
				if (wp.Distance(wpList[i].point) >250) return false;
			}
			
			return true;
		}
		
		/*
		protected Composite CreateBehavior_QuestbotMain()
        {
            return _root ?? (_root =
                new Decorator(ret => !IsDone && (!AllowCombat || !Me.Combat),
                        new Action(delegate
                        {
                            Navigator.MoveTo(Path.Peek());
                        })
                    )
                )
        }
		*/
		
		
		public static bool IsViable(WoWObject wowObject)
        {
            return (wowObject != null) && wowObject.IsValid;
        }
		
		public static IEnumerable<WoWUnit> UnfriendlyUnits(int maxSpellDist)
        {
            
            Type typeWoWUnit = typeof(WoWUnit);
            Type typeWoWPlayer = typeof(WoWPlayer);
            List<WoWUnit> list = new List<WoWUnit>();
            List<WoWObject> objectList = ObjectManager.ObjectList;
            for (int i = 0; i < objectList.Count(); i++)
            {
                Type type = objectList[i].GetType();
                if (type == typeWoWUnit || type == typeWoWPlayer)
                {
                    WoWUnit t = objectList[i] as WoWUnit;
                    if (t != null && fightWithMe(t))
                    {
                        list.Add(t);
                    }
                }
            }
            return list;
        }
		public static IEnumerable<WoWUnit> NearbyUnfriendlyUnits
        {
            get
            {
                return UnfriendlyUnits(100);
            }
        }
		
		private double LootRange
        {
            get { return LootTargeting.LootRadius; }
        }
        private IOrderedEnumerable<WoWUnit> Lootables
        {
            get
            {
                var targetsList = ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(
                        p => p.Lootable && p.Distance <= LootRange).OrderBy(l => l.Distance);
                return targetsList;
            }
        }
		
		public class WayPoint
		{	
			public WoWGuid guid { get; set; }
			public WoWPoint point { get; set; }
			
			public WayPoint(WoWGuid _guid, WoWPoint _point)
			{
				guid = _guid;
				point = _point;
			}
		}
	}
	
}

